package org.batatasframework.bytecode;

import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.batatasframework.bytecode.spi.AsmInstancable;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.util.ClassUtils;

/**
 * 一个类的asm信息
 * @author jackson
 *
 */
public class ClassAsminfo implements AsmInstancable{
	
	public static final ClassAsminfo T_VOID = new ClassAsminfo(void.class);
	
	public static final ClassAsminfo T_STRING = new ClassAsminfo(String.class);
	public static final ClassAsminfo T_DATE = new ClassAsminfo(Date.class);
	public static final ClassAsminfo T_NUMBER = new ClassAsminfo(Number.class);
	public static final ClassAsminfo T_INTEGER = new ClassAsminfo(Integer.class);
	public static final ClassAsminfo T_LONG = new ClassAsminfo(Long.class);
	public static final ClassAsminfo T_DOUBLE = new ClassAsminfo(Double.class);
	
	public static final ClassAsminfo T_I = new ClassAsminfo(String.class);
	public static final ClassAsminfo T_J = new ClassAsminfo(String.class);
	
	public static final ClassAsminfo A_OBJECT = new ClassAsminfo(Object[].class);
	
	/** 这个字段不一定有值，如：动态生成新的类的时候，这个对应在处理过程中为null */
	private Class<?> clazz;
	private final String classPath;
	private final String className;
	private boolean isInterface;
	
	private List<MethodAsminfo> methods = new ArrayList<>();
	private List<FieldAsminfo>  fields = new ArrayList<>();

	public static final int parseModifiers(Member member){
		int modifiers = member.getModifiers();
		if(Modifier.isPrivate(modifiers)) return Opcodes.ACC_PRIVATE;
		if(Modifier.isPublic(modifiers)) return Opcodes.ACC_PUBLIC;
		return Opcodes.ACC_PROTECTED;
	}

	public static ClassAsminfo[] toArray(Class<?>... pts){
		ClassAsminfo[] cis = new ClassAsminfo[pts.length];
		for(int i = 0; i < pts.length; i++){
			cis[i] = new ClassAsminfo(pts[i]);
		}
		return cis;
	}
	
	public ClassAsminfo(Class<?> clazz){
		this(clazz.getName(), Type.getInternalName(clazz), clazz.isInterface());
		this.clazz = clazz;
	}
	
	protected ClassAsminfo(String className, String classPath, boolean isInterface){
		this.className = className;
		this.classPath = classPath;
		this.isInterface = isInterface;
	}
	
	public ClassAsminfo(String className){
		this(className, ClassUtils.convertClassNameToResourcePath(className), false);
	}
	
	/**
	 * <pre>
	 * 创建当前类型的一个对象实例
	 * 
	 * 注意：需要处理构造函数的参数问题
	 * 
	 * 这个是调用无参的构造函数
	 * </pre>
	 */
	public void instance(MethodVisitor visitor){
		if(null != clazz){
			if(clazz.isPrimitive()){
				/* 基本类型的数据不需要创建对象，只需要直接赋值即可 */
				return;
			}
			
			if(clazz.isArray()){
				/* 创建一个数组 */
				
				return;
			}
		}
		visitor.visitTypeInsn(Opcodes.NEW, classPath);
		visitor.visitInsn(Opcodes.DUP);
		visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, classPath, "<init>", "()V", false);
		
	}
	
	public FieldAsminfo assignField(String name) throws Exception{
		if(null == clazz) return null;
		return assignField(new FieldAsminfo(this, clazz.getField(name)));
	}

	/**
	 * <pre>
	 * 用于存储当前类型的一个类型，
	 * 
	 * 如：visitor.visitVarInsn(ASTORE, variable)
	 * </pre>
	 */
	public int insnStore(){
		if(null != clazz){
			if(clazz.isPrimitive()){
				if(int.class.equals(clazz)){
					return Opcodes.ISTORE;
				}else if(long.class.equals(clazz)){
					return Opcodes.LSTORE;
				}else if(double.class.equals(clazz)){
					return Opcodes.DSTORE;
				}else if(char.class.equals(clazz)){
					return Opcodes.ISTORE;
				}else if(float.class.equals(clazz)){
					return Opcodes.FSTORE;
				}else if(short.class.equals(clazz)){
					return Opcodes.ISTORE;
				}else{//boolean and byte 都是用 BASTORE
					return Opcodes.ISTORE;
				}
			}else if(clazz.isArray()){
				Class<?> nextClazz = clazz.getComponentType();
				if(nextClazz.isPrimitive()){
					if(int.class.equals(nextClazz)){
						return Opcodes.IASTORE;
					}else if(long.class.equals(nextClazz)){
						return Opcodes.LASTORE;
					}else if(double.class.equals(nextClazz)){
						return Opcodes.DASTORE;
					}else if(char.class.equals(nextClazz)){
						return Opcodes.CASTORE;
					}else if(float.class.equals(nextClazz)){
						return Opcodes.FASTORE;
					}else if(short.class.equals(nextClazz)){
						return Opcodes.SASTORE;
					}else{//boolean and byte 都是用 BASTORE
						return Opcodes.BASTORE;
					}
				}
				return Opcodes.AASTORE;
			}
		}
		return Opcodes.ASTORE;
	}
	
	public int insnLoad(){
		if(null != clazz){
			if(clazz.isPrimitive()){
				if(int.class.equals(clazz)){
					return Opcodes.ILOAD;
				}else if(long.class.equals(clazz)){
					return Opcodes.LLOAD;
				}else if(double.class.equals(clazz)){
					return Opcodes.DLOAD;
				}else if(char.class.equals(clazz)){
					return Opcodes.ILOAD;
				}else if(float.class.equals(clazz)){
					return Opcodes.FLOAD;
				}else if(short.class.equals(clazz)){
					return Opcodes.ILOAD;
				}else{//boolean and byte 都是用 BALOAD
					return Opcodes.ILOAD;
				}
			}else if(clazz.isArray()){
				Class<?> nextClazz = clazz.getComponentType();
				if(nextClazz.isPrimitive()){
					if(int.class.equals(nextClazz)){
						return Opcodes.IALOAD;
					}else if(long.class.equals(nextClazz)){
						return Opcodes.LALOAD;
					}else if(double.class.equals(nextClazz)){
						return Opcodes.DALOAD;
					}else if(char.class.equals(nextClazz)){
						return Opcodes.CALOAD;
					}else if(float.class.equals(nextClazz)){
						return Opcodes.FALOAD;
					}else if(short.class.equals(nextClazz)){
						return Opcodes.SALOAD;
					}else{//boolean and byte 都是用 BALOAD
						return Opcodes.BALOAD;
					}
				}
				return Opcodes.AALOAD;
			}
		}
		return Opcodes.ALOAD;
	}
	
	public FieldAsminfo assignField(int access, ClassAsminfo typeClass){
		return assignField(access, "f_" + fields.size(), typeClass);
	}

	public FieldAsminfo assignField(ClassAsminfo typeClass){
		return assignField(Opcodes.ACC_PROTECTED, typeClass);
	}
	
	public FieldAsminfo assignField(int access, String name, ClassAsminfo typeClass){
		return assignField(new FieldAsminfo(this, access, name, typeClass));
	}
	
	public FieldAsminfo assignField(FieldAsminfo fasminfo){
		fields.add(fasminfo);
		return fasminfo;
	}
	
	void add(MethodAsminfo method){
		methods.add(method);
	}
	
	public MethodAsminfo getMethod(String name, Class<?>...pts){
		try{
			for(MethodAsminfo method : methods){
				if(method.check(name, pts)) return method;
			}
			return new MethodAsminfo(this, name, pts);
		}catch (Exception e) {
			return null;
		}
	}
	
	public boolean ckeck(Class<?> type){
		return null != clazz && clazz.equals(type);
	}
	
	public String getClassName(){
		return className;
	}
	
	public String getClassPath() {
		return classPath;
	}
	
	public boolean isInterface() {
		return isInterface;
	}
	
	/**
	 * <pre>
	 * 生成相应的类型的描述，如："Ljava.lang.String;" 或 "I;" 等; 
	 * 如果是void，则返回"V"
	 * </pre>
	 */
	public String toDes(){
		if(null != clazz){
			if(clazz == void.class){
				return "V";
			}else if(clazz.isPrimitive()){
				return classPath + ";";
			}
		}
		return "L" + classPath + ";";
	}
	
	
	@Override
	public String toString() {
		return classPath;
	}
	
	/** 
	 * <pre>
	 * 把其它类型的值强转为当前类型的值，
	 * 如： Object obj = ...;
	 * String str = (String)obj;
	 * 
	 * 注意：本语句只相当于"(String)obj"部分，并没有使用变量把结果存储起来
	 * </pre>
	 */
	public void cast(MethodVisitor visitor){
		visitor.visitTypeInsn(Opcodes.CHECKCAST, classPath);
	}
	
	/**
	 * <pre>
	 * 参考：{@link #cast(MethodVisitor)}
	 * 
	 * 这里会进行判断，如果两个类型无法直接赋值，则会进行类型的强制转换
	 * </pre>
	 */
	public void cast(MethodVisitor visitor, ClassAsminfo sourceType){
		if(!isAssignableFrom(sourceType)){
			/* 需要进行转换 */
			visitor.visitTypeInsn(Opcodes.CHECKCAST, classPath);
		}
	}

	public boolean isAssignableFrom(ClassAsminfo cls){
		return clazz.isAssignableFrom(cls.clazz);
	}
	
	public Class<?> getType(){
		return clazz;
	}
}
